package com.chushouya.aop;

import com.chushouya.common.support.Contexts;
import com.chushouya.common.support.RequestHeaderContext;
import com.chushouya.common.support.RequestHeaders;
import com.general.framework.core.lang.Jsons;
import com.general.framework.core.lang.Numbers;
import com.general.framework.core.lang.Strings;
import com.general.framework.core.utils.ThreadContext;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.InputStreamSource;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * 上下文信息处理拦截器
 */
@Slf4j
@Aspect
@Component
@Order(201)
public class WebAspect {

    @Resource
    private HttpServletRequest request;

    /**
     * 上下文信息处理
     */
    @Around("@within(org.springframework.web.bind.annotation.RestController)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        final long oldTime = System.currentTimeMillis();
        // 构建头部信息
        final RequestHeaderContext headerContext = this.buildHeaderContext();
        ThreadContext.putValue(Contexts.REQUEST_HEADER_CONTEXT_KEY, headerContext);
        final String accessUrl = headerContext.getUrl();
        try {
            return joinPoint.proceed();
        } finally {
            ThreadContext.removeValue(Contexts.REQUEST_HEADER_CONTEXT_KEY);
            final String params = this.formatParam(joinPoint);
            log.info("==> {} time={}ms", accessUrl, System.currentTimeMillis() - oldTime);
            if (Strings.isNotBlank(params)) {
                log.info("==> {}", params);
            }
        }
    }

    /**
     * 获取请求头信息
     */
    private RequestHeaderContext buildHeaderContext() {
        final String headerIp = request.getHeader(RequestHeaders.CLIENT_IP);
        final String headerPartnerId = request.getHeader(RequestHeaders.PARTNER_ID);
        final RequestHeaderContext headerContext = new RequestHeaderContext();
        if(Strings.isNotEmpty(headerPartnerId)) {
            headerContext.setPartnerId(Long.valueOf(headerPartnerId));
        }
//        headerContext.setIp(Strings.isBlank(headerIp) ? IpUtils.getIpAddr(request) : headerIp);
        headerContext.setUrl(request.getRequestURI());
        headerContext.setOsType(request.getHeader(RequestHeaders.OS_TYPE));
        headerContext.setOsVersion(request.getHeader(RequestHeaders.OS_VERSION));
        headerContext.setPlatform(request.getHeader(RequestHeaders.PLATFORM));
        headerContext.setAppVersion(request.getHeader(RequestHeaders.APP_VERSION));
        headerContext.setDeviceId(request.getHeader(RequestHeaders.DEVICE_ID));
        headerContext.setToken(request.getHeader(RequestHeaders.TOKEN));
        headerContext.setAppId(request.getHeader(RequestHeaders.APP_ID));
        headerContext.setTimestamp(request.getHeader(RequestHeaders.TIMESTAMP));
        headerContext.setSign(request.getHeader(RequestHeaders.SIGN));
        return headerContext;
    }

    /**
     * 获取格式化参数
     */
    private String formatParam(final JoinPoint joinPoint) {
        final Object[] paramValues = joinPoint.getArgs();
        if (paramValues == null || paramValues.length < Numbers.INTEGER_ONE) {
            return Strings.EMPTY;
        }
        String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
        final Map<String, Object> paramMap = new HashMap<>(paramValues.length);
        for (int i = 0; i < paramValues.length; i++) {
            final Object paramValue = paramValues[i];
            if (!isPrint(paramValue)) {
                continue;
            }
            paramMap.put(paramNames[i], paramValue);
        }
        return Jsons.toJsonString(paramMap);
    }


    /**
     * 是否打印参数
     */
    private boolean isPrint(Object target) {
        if (target == null) {
            return true;
        }
        if (target instanceof InputStreamSource || target instanceof ServletRequest || target instanceof ServletResponse) {
            return false;
        }
        return true;
    }

}
